package com.aerors.sbgt.util;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import com.aerors.sbgt.entity.*;

/**
 * 切片计算器
 * 
 * @author tianhui
 *
 */
public class TileCalculator {
	private static TileCalculator tileCalculator = new TileCalculator();

	public TileCalculator() {
	}

	public static TileCalculator getInstance() {
		return tileCalculator;
	}

	public static void main(String[] args) {
		int colNumber = 2;
		int rowNumber = 1;
		int scale = 1;
		System.out.println(tile2lon(colNumber, scale));
		System.out.println(tile2lat(rowNumber, scale));
	}

	/**
	 * 获取行列号
	 * 
	 * @param scaleMin
	 * @param scaleMax
	 * @param latMin
	 * @param latMax
	 * @param lonMin
	 * @param lonMax
	 * @return
	 */
	public static List<TileInfo> getOSMTileMatrix(int scaleMin, int scaleMax, double latMin, double latMax,
			double lonMin, double lonMax) {
		List<TileInfo> OSMtileMatrixList = new ArrayList<TileInfo>();
		for (int i = scaleMin; i < scaleMax; i++) {
			int tileRowMax = getOSMTileYFromLatitude(latMin, i);
			int tileRowMin = getOSMTileYFromLatitude(latMax, i);
			int tileColMin = getOSMTileXFromLongitude(lonMin, i);
			int tileColMax = getOSMTileXFromLongitude(lonMax, i);
			for (int k = tileRowMin; k <= tileRowMax; k++) {
				for (int j = tileColMin; j <= tileColMax; j++) {
					TileInfo temp = new TileInfo();
					temp.rowNumber = String.valueOf(k);
					temp.scale = String.valueOf(i);
					temp.columnNumber = String.valueOf(j);
					temp.bbox = tile2boundingBox(j,k,i);
					temp.hexRowNumber = getHexString(Integer.toHexString(k));
					temp.hexColumnNumber = getHexString(Integer.toHexString(j));
					OSMtileMatrixList.add(temp);
				}
			}
		}
		OSMtileMatrixList = removeDuplicate(OSMtileMatrixList);
		System.out.println("理论切片数量:" + OSMtileMatrixList.size());
		return OSMtileMatrixList;
	}

	/**
	 * 通过经度计算切片列号
	 * 
	 * @param lon
	 * @param zoom
	 * @return
	 */
	private static int getOSMTileXFromLongitude(double lon, int zoom) {
		return (int) (Math.floor((lon + 180) / 360 * Math.pow(2, zoom)));
	}

	/**
	 * 通过纬度计算切片行号
	 * 
	 * @param lat
	 * @param zoom
	 * @return
	 */
	private static int getOSMTileYFromLatitude(double lat, int zoom) {
		return (int) (Math
				.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2
						* Math.pow(2, zoom)));
	}

	/**
	 * 将16进制补足8位
	 * 
	 * @param hexStr
	 * @return
	 */
	private static String getHexString(String hexStr) {
		int length = hexStr.length();
		int numCount = 8;
		String leftZero = "";
		if (length < numCount) {
			int leftCount = numCount - length;
			for (int j = 0; j < leftCount; j++) {
				leftZero += "0";
			}
		}
		return leftZero.concat(hexStr);
	}

	/**
	 * 删除ArrayList中重复元素
	 * 
	 * @param list
	 */
	private static List removeDuplicate(List list) {
		HashSet h = new HashSet(list);
		list.clear();
		list.addAll(h);
		return list;
	}

	/**
	 * 根据行列号获取切片的地理范围
	 * @param row
	 * @param col
	 * @param zoom
	 * @return
	 */
	private static BoundingBox tile2boundingBox(final int col, final int row, final int zoom) {
		BoundingBox bb = new BoundingBox();
		bb.north = tile2lat(row, zoom);
		bb.south = tile2lat(row + 1, zoom);
		bb.west = tile2lon(col, zoom);
		bb.east = tile2lon(col + 1, zoom);
		return bb;
	}

	private static double tile2lon(int x, int z) {
		return x / Math.pow(2.0, z) * 360.0 - 180;
	}

	private static double tile2lat(int y, int z) {
		double n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z);
		return Math.toDegrees(Math.atan(Math.sinh(n)));
	}
}
